Oracle 加密package解密(unwrap)

最近碰到一个问题,需要解密几个使用oracle wrap加密过的程序包,查了下,已经有很多可用的程序,支持10g,11g,连12c都支持。查找过程中,花了点时间研究了下解密的过程,简单记录一下。

1. 解密算法解析

要解密,首先要了解加密的原理,在这几个版本的oracle中,解密的理论依据都来源于 "The oracle hacker's handbook" by David Litchfield 这本书,书中介绍了wrap的过程:

  1. 将源码使用lz压缩算法压缩,得到压缩串lzstr
  1. 对压缩串做sha-1运算,得到40位的哈希串shstr
  2. 将哈希串与压缩串拼在一起,shstr+lzstr
  3. 将得到的字符串再按字节执行字典表替换
  4. 再执行base64编码,就得到了加密的字符串

如果要解密加密以后的字符串,按照以上加密的过程反向推算,可以发现最关键的就是第4步中的字典表替换过程,要解决这个问题,我们就要得到这个替换的字典表,幸运的是,经过测试发现,oracle的这个字典表是固定的按字节映射,所以:
我们可以通过wrap加密一个简单字符串,比如create PACKAGE a0,得到加密后的密文,然后执行一次base64解码,就能得到替换后的字符串,代码如下

select substr(utl_encode.base64_decode(utl_raw.cast_to_raw(
           rtrim(substr(wrap_text,
           instr(wrap.wrap,chr(10),1,20) + 1),chr(10)))),
       41) from dual

这里说明一下,wrap以后的密文里,前面20行是没有用的,密文是从第21行开始的,第21行的前40个字符,是拼接的哈希串,从第41个字符开始才是字典表转换后的密文
本例中,得到的结果为

78DA0B7074F67674775548346000001185029E

对字符串create PACKAGE a0执行lz压缩,得到字典表转换前的字符串,本例中的结果为

308399B8F5339FF5BF5C5A2170A6A6F302E141

这样我们得到了转换前和转换后的字符串,就可以按字节建立起我们需要的字典表了,比如本例中:

转换前-转换后
30        78
83        DA

依次类推,我们就可以建立起这个字典表,因为一个字节从00-FF有256个字符,所以相应的这个典表应该有256条记录,这里我们只得到了其中的一部分,还需要选用不同的字符串,多次穷举运算,最终得到全部的记录。事实上,因为这个字典表是固定的,所以得到的结果可以重复使用。当然也可以直接使用别人提供的。
有了字典表,接下来的工作就比较简单了,按照加密过程反向运算即可:

  1. 从数据库中得到package的密文
  1. base64解码
  2. 字典表反向映射
  3. 去掉前40位哈希串
  4. lz解压缩
2. 解密程序源码

这里贴上部分源码,供参考
LZ解压和压缩的代码,使用java source过程,为了支持比较大的package,使用CLOB作为参数类型,经测试,可以支持较大的程序包

create or replace java source named CUX_UNWRAPPER
as
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.zip.Deflater;
import java.util.zip.InflaterInputStream;
import oracle.jdbc.OracleDriver;
import oracle.sql.CLOB;

public class UNWRAPPER {
    //解压缩
    public static CLOB Inflate(CLOB src) throws IOException, SQLException {
        StringBuffer sb = new StringBuffer();
        String s = src.stringValue();
         try{
        ByteArrayInputStream bis = 
            new ByteArrayInputStream(decodeHex(s.toCharArray()));
        //ByteArrayInputStream bis = new ByteArrayInputStream(src);
        InflaterInputStream iis = new InflaterInputStream(bis);
        for (int c = iis.read(); c != -1; c = iis.read()) {
            sb.append((char)c);
        }
        }catch (Exception e)
        {
           e.printStackTrace();
        }
       
        Connection conn = DriverManager.getConnection("jdbc:default:connection:");
        CLOB clob =  CLOB.createTemporary(conn, false, CLOB.DURATION_SESSION);
        clob.setString(1, sb.toString());
        return clob;

    }

    public static byte[] Deflate(String src, int quality) {
        try {
            byte[] tmp = new byte[src.length() + 100];
            Deflater defl = new Deflater(quality);
            defl.setInput(src.getBytes("UTF-8"));
            defl.finish();
            int cnt = defl.deflate(tmp);
            byte[] res = new byte[cnt];
            for (int i = 0; i < cnt; i++)
                res = tmp;
            return res;
        } catch (Exception e) {
        }
        return null;
    }

    public static int toDigit(char ch, int index) {
        int digit = Character.digit(ch, 16);
        if (digit == -1) {
            throw new RuntimeException("illegal hexadecimal character " + ch + 
                                       " at index " + index);
        }
        return digit;
    }
    
    //16进制字符串转字节数组
    public static byte[] decodeHex(char[] data) {
        int len = data.length;
        if ((len & 0x01) != 0) {
            throw new RuntimeException("odd  number of characters ");
        }

        byte[] out = new byte[len >> 1];

        for (int i = 0, j = 0; j < len; i++) {
            int f = toDigit(data[j], j) << 4;
            j++;
            f = f | toDigit(data[j], j);
            j++;
            out[i] = (byte)(f & 0xFF);
        }

        return out;
    }
}
/

--编译java source
alter java source CUX_UNWRAPPER compile
/

解密plsql主程序

--程序包体
create or replace package body cux_unwrapper is
  function deflate(src in varchar2) return raw is
  begin
    return deflate(src, 6);
  end;

  --压缩
  function deflate(src in varchar2, quality in number) return raw as
    language java name 'UNWRAPPER.Deflate( java.lang.String, int ) return byte[]';

  --解压缩
  function inflate(src in clob) return clob as
    language java name 'UNWRAPPER.Inflate( oracle.sql.CLOB ) return oracle.sql.CLOB';
  
  --输出clob
  procedure out_put(p_clob clob) is
    l_len     integer;
    l_pos     integer := 1;
    l_chunk   integer := 2000;
    l_buffer  varchar2(4000);
    l_amount  integer;
    l_ins_pos integer := 1;
  begin
    l_len := dbms_lob.getlength(p_clob);
    while l_pos < l_len loop
    
      if l_len - l_pos < l_chunk then
        l_ins_pos := l_len;
      else
        l_ins_pos := dbms_lob.instr(p_clob, chr(10), l_pos + l_chunk, 1);
      end if;
    
      l_amount := l_ins_pos - l_pos + 1;
      dbms_lob.read(p_clob, l_amount, l_pos, l_buffer);
      dbms_output.put_line(l_buffer);
      l_pos := l_ins_pos + 1;
    end loop;
  end out_put;

  --解密主程序
  PROCEDURE unwrap(p_owner IN VARCHAR,
                   p_name  IN VARCHAR,
                   p_type  IN VARCHAR) AS
  
    l_wrap varchar2(32767);
  
    l_inf       clob;
    l_res       clob;
    l_src       clob;
    l_inflate   varchar2(32767);
    l_temp      VARCHAR2(32767);
    l_bt        varchar2(32767);
    l_text      varchar2(32767);
    l_char      varchar2(2);
    l_len       number;
    l_slen      integer;
    l_offset    integer := 1;
    l_chunk_len integer := 10080;
  
    --解密字节对照字典表
    l_dict varchar2(512) := '3D6585B318DBE287F152AB634BB5A05F' ||
                            '7D687B9B24C228678ADEA4261E03EB17' ||
                            '6F343E7A3FD2A96A0FE935561FB14D10' ||
                            '78D975F6BC4104816106F9ADD6D5297E' ||
                            '869E79E505BA84CC6E278EB05DA8F39F' ||
                            'D0A271B858DD2C38994C480755E4538C' ||
                            '46B62DA5AF322240DC50C3A1258B9C16' ||
                            '605CCFFD0C981CD4376D3C3A30E86C31' ||
                            '47F533DA43C8E35E1994ECE6A39514E0' ||
                            '9D64FA5915C52FCABB0BDFF297BF0A76' ||
                            'B449445A1DF0009621807F1A82394FC1' ||
                            'A7D70DD1D8FF139370EE5BEFBE09B977' ||
                            '72E7B254B72AC7739066200E51EDF87C' ||
                            '8F2EF412C62B83CDACCB3BC44EC06936' ||
                            '6202AE88FCAA4208A64557D39ABDE123' ||
                            '8D924A1189746B91FBFEC901EA1BF7CE';
    l_sl   varchar2(512) := '000102030405060708090A0B0C0D0E0F' ||
                            '101112131415161718191A1B1C1D1E1F' ||
                            '202122232425262728292A2B2C2D2E2F' ||
                            '303132333435363738393A3B3C3D3E3F' ||
                            '404142434445464748494A4B4C4D4E4F' ||
                            '505152535455565758595A5B5C5D5E5F' ||
                            '606162636465666768696A6B6C6D6E6F' ||
                            '707172737475767778797A7B7C7D7E7F' ||
                            '808182838485868788898A8B8C8D8E8F' ||
                            '909192939495969798999A9B9C9D9E9F' ||
                            'A0A1A2A3A4A5A6A7A8A9AAABACADAEAF' ||
                            'B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF' ||
                            'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF' ||
                            'D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF' ||
                            'E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF' ||
                            'F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF';
    cursor c_src is
      SELECT src.line, src.text
        FROM DBA_SOURCE src
       WHERE owner = p_owner
         AND Name = p_name
         AND TYPE = p_type
       order by line;
  BEGIN
    dbms_lob.createtemporary(lob_loc => l_inf,
                             cache   => TRUE,
                             dur     => dbms_lob.session);
    --取得package密文
    for rec in c_src loop
      l_src := l_src || rtrim(rec.text);
    end loop;
  
     --out_put('source:<'||l_src||'>');
    l_src := rtrim(substr(l_src, instr(l_src, chr(10), 1, 20) + 1), chr(10));
    l_src := replace(l_src, chr(10), '');
    -- dbms_output.put_line('source:<'||l_src||'>');
    --l_src:=substr(l_src,41);
    
    --base64解码
    l_len := dbms_lob.getlength(l_src);
    while l_offset < l_len loop
      if (l_len - l_offset) < 10080 then
        l_chunk_len := (l_len - l_offset);
      end if;
      l_temp := dbms_lob.substr(l_src, l_chunk_len, l_offset);
      l_bt   := utl_encode.base64_decode(utl_raw.cast_to_raw(l_temp));
    
      if l_bt is not null then
        if l_offset = 1 then
          --去掉前40位哈希串
          l_bt := substr(l_bt, 41);
        end if;
        -- l_wrap := l_wrap || l_bt;
        --字典表转换
        l_bt := utl_raw.translate(l_bt, l_sl, l_dict);
      
        l_offset := l_offset + l_chunk_len;
        --    l_inflate := l_inflate || l_bt;
        dbms_lob.writeappend(l_inf, length(l_bt), l_bt);
      end if;
    
    end loop;
  
    /* dbms_output.put_line('base:' || l_wrap);
    dbms_output.put_line('<' || l_inflate || '>');*/
    
    --解压缩
    l_res := inflate(l_inf);
    out_put(l_res);
  END unwrap;
END;
/
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 背景: 阅读新闻 12C CDB模式下RMAN备份与恢复 [日期:2016-11-29] 来源:Linux社区 作...
    阳屯okyepd阅读 3,234评论 0 7
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 本文主要介绍移动端的加解密算法的分类、其优缺点特性及应用,帮助读者由浅入深地了解和选择加解密算法。文中会包含算法的...
    苹果粉阅读 11,305评论 5 29
  • CTF中那些脑洞大开的编码和加密 0x00 前言 正文开始之前先闲扯几句吧,玩CTF的小伙伴也许会遇到类似这样的问...
    查无此人asdasd阅读 5,769评论 0 19
  • 天还是被雾霾笼罩着,灰蒙蒙的,仍然看不见从前的蓝天,但是过的理所当然,毫无愧疚。也许这就是冷血动物吧!还是好面的强...
    麦子上的土地阅读 197评论 0 0